#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UTable.Input;
using System.Windows;
using UTable.ObjectManager.Commands;
using System.Windows.Media;

namespace UTable.Objects.Policies
{
    public class LayoutPolicy: Policy
    {

        #region Properties

        /// <summary>
        /// Gets or sets the ObjectOutDetector which can 
        /// invokes an ObjectOutDetectedEvent when a sub-object
        /// moves out of the current object
        /// </summary>
        public ObjectOutDetector ObjectOutDetector { get; set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="target">The target of the policy</param>
        /// <param name="param">The parameter of the policy</param>
        public LayoutPolicy(IObject target, object param): base(target, param)
        {
        }

        #endregion

        #region Protected Methods

        protected void MoveObject(IObject target, IObject c)
        {
            if (c == null)
                return;
            Point p1 = target.TranslatePoint(new Point(0, 0), c);
            Point p2 = target.TranslatePoint(new Point(target.Width, 0), c);
            Point p3 = target.TranslatePoint(new Point(target.Width, target.Height), c);
            Point p4 = target.TranslatePoint(new Point(0, target.Height), c);
            // OnObjectRemoved(target);
            if (target.Owner != null)
                UTableHelper.PutCommand(new ObjectCommand(target.Owner, ObjectCommandType.Remove, target));
            if (!c.OwnedObjects.Contains(target))
            {
                Point center = new Point((p1.X + p3.X) / 2, (p1.Y + p3.Y) / 2);
                Point refPoint = new Point((p2.X + p3.X) / 2, (p2.Y + p3.Y) / 2);
                double angle = GetAngle(refPoint - center);
                Point originalPoint = RotatePoint(center, p1, angle, false);

                target.Orientation = -angle * 180 / Math.PI;
                target.ScaleX = target.ScaleX / c.ScaleX;
                target.ScaleY = target.ScaleY / c.ScaleY;
                double x1 = (originalPoint.X - center.X) / target.ScaleX + center.X;
                double x2 = (originalPoint.Y - center.Y) / target.ScaleY + center.Y;
                double positionX = (originalPoint.X - center.X) / target.ScaleX + center.X;
                double positionY = (originalPoint.Y - center.Y) / target.ScaleY + center.Y;
                target.Position = new Point(positionX, positionY);
                UTableHelper.PutCommand(new ObjectCommand(c, ObjectCommandType.Insert, target));
            }
        }

        // Get the angle of the v (clockwise, [0, 2PI])
        protected double GetAngle(Vector v)
        {
            double x = v.X;
            double y = v.Y;
            double ang = Math.Acos(x / v.Length);
            if (y > 0)
                ang = Math.PI * 2 - ang;
            return ang;
        }

        protected Point RotatePoint(Point referencePoint, Point rotatePoint, double ang, bool isClockwise)
        {
            if (isClockwise)
                ang = 2 * Math.PI - ang;
            double x = rotatePoint.X - referencePoint.X;
            double y = rotatePoint.Y - referencePoint.Y;
            double cosTheta = Math.Cos(ang);
            double sinTheta = Math.Sin(ang);
            Point p = new Point();
            p.X = (cosTheta * x - sinTheta * y) + referencePoint.X;
            p.Y = (cosTheta * y + sinTheta * x) + referencePoint.Y;
            return p;
        }

        #endregion

        #region ILayoutPolicy Members

        public virtual void OnClosed()
        {

        }

        /// <summary>
        /// Update the parameter of the this layout policy
        /// This methods will be called 
        /// when the LayoutPolicyParameter property of an UObject is set
        /// </summary>
        /// <param name="param">Parameter for the Policy</param>
        public override void UpdateParameter(object param)
        {

        }

        /// <summary>
        /// Called when a sub-object received input
        /// </summary>
        public virtual void OnInputReceived(IObject obj, InputEventArgs args)
        {
        }

        /// <summary>
        /// Called when the Position property of a sub-child is set
        /// </summary>
        public virtual void TranslateObject(IObject obj, Point p)
        {
            obj.Position = p;
        }

        /// <summary>
        /// Called when the orientation property of a sub-object is set
        /// </summary>
        public virtual void RotateObject(IObject obj, double angle)
        {
            obj.Orientation = angle;
        }

        /// <summary>
        /// called when the Width property or Height property of a sub-object is set
        /// </summary>
        public virtual void ResizeObject(IObject obj, Size size)
        {
            obj.Width = size.Width;
            obj.Height = size.Height;
        }

        /// <summary>
        /// called when the ScaleX property of ScaleY property of a sub-object is set
        /// </summary>
        public virtual void ScaleObject(IObject obj, Size scale)
        {
            obj.ScaleX = scale.Width;
            obj.ScaleY = scale.Height;
        }

        /// <summary>
        /// called when the MinWidth property or MinHeight property of a sub-object is set
        /// </summary>
        public virtual void ChangeMinSize(IObject obj, Size size)
        {
            obj.MinWidth = size.Width;
            obj.MinHeight = size.Height;
        }

        /// <summary>
        /// called when the MaxWidth property or MaxHeight property of a sub-object is set
        /// </summary>
        public virtual void ChangeMaxSize(IObject obj, Size size)
        {
            obj.MaxHeight = size.Height;
            obj.MaxWidth = size.Width;
        }

        /// <summary>
        /// called when the ScaleCenter property of a sub-object is set
        /// </summary>
        public virtual void ChangeScaleCenter(IObject obj, Point p)
        {
            obj.ScaleCenter = p;
        }

        /// <summary>
        /// called when the RotateCenter property of a sub-object is set
        /// </summary>
        public virtual void ChangeRotateCenter(IObject obj, Point p)
        {
            obj.RotateCenter = p;
        }

        /// <summary>
        /// called when the ZIndex property of a sub-object is set
        /// </summary>
        public virtual void ChangeZIndex(IObject obj, int zIndex)
        {
            obj.ZIndex = zIndex;
        }

        /// <summary>
        /// called when a sub-object is removed from current object
        /// </summary>
        public virtual void OnObjectRemoved(IObject obj)
        {
            // This is done in global object manager now
            /*
            if (this.Target != null)
                (this.Target as IObjectEntity).Children.Remove(obj as IObjectEntity);
             */
        }

        /// <summary>
        /// called when a sub-object is inserted into current object
        /// </summary>
        public virtual void OnObjectInserted(IObject obj)
        {
            // This is done in global object manager now
         /*
            if (this.Target != null && !this.Target.OwnedObjects.Contains(obj))
                (this.Target as IObjectEntity).Children.Add(obj as IObjectEntity);*/
                
        }

        /// <summary>
        /// called when a sub-object is closed
        /// </summary>
        public virtual void OnObjectClosed(IObject obj)
        {
            // This is done in global object manager now
            /*
                if (this.Target != null)
                                (this.Target as IObjectEntity).Children.Remove(obj as IObjectEntity);*/
                
        }

        /// <summary>
        /// Called when join two sub-objects request is sent
        /// </summary>
        public virtual void JoinObjects(ObjectJoinCommandArgs args)
        {
            // should be implemented by the inherited class
        }

        /// <summary>
        /// Called when unjoin two sub-objects request is sent
        /// </summary>
        public virtual void UnjoinObjects(ObjectUnjoinCommandArgs args)
        {
            // should be implemented by the inherited class
        }

        /// <summary>
        /// Called when ObjectLayoutState Property is set as Maximized
        /// </summary>
        internal virtual void MaximizeObject(IObject obj)
        {
            // should be implemented by the inherited class
        }

        /// <summary>
        /// Called when ObjectLayoutState Property is set as Minimized
        /// </summary>
        internal virtual void MinimizeObject(IObject obj)
        {
            // should be implemented by the inherited class
        }

        /// <summary>
        /// Called when ObjectLayoutState Property is set as Normal
        /// </summary>
        internal virtual void RestoreObject(IObject obj)
        {
            // should be implemented by the inherited class
        }

        internal virtual void CopyObject(IObject obj)
        {
            // should be implemented by the inherited class
        }

        /// <summary>
        /// Called when ObjectLayoutState Property of a sub-object is set
        /// </summary>
        public virtual void ChangeLayoutState(IObject obj, ObjectLayoutState state)
        {
            obj.LayoutState = state;
        }

        /// <summary>
        /// Called when OperatingState Property of a sub-object is set
        /// </summary>
        public virtual void ChangeOperatingState(IObject obj, ObjectOperatingState state)
        {
            obj.OperatingState = state;
        }

        /// <summary>
        /// Called when Visibility Property of a sub-object is set
        /// </summary>
        public virtual void ChangeVisibility(IObject obj, Visibility visibility)
        {
            obj.Visibility = visibility;
        }

        public virtual void ChangeClip(IObject obj, Geometry clip)
        {
            obj.Clip = clip;
        }
        #endregion
    }
}
